package br.com.gscorp.bankLion.controller;

import java.io.IOException;
import java.util.List;

import javax.validation.Valid;

import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import br.com.gscorp.bankLion.dto.CadastroUsuarioForm;
import br.com.gscorp.bankLion.dto.UsuarioForm;
import br.com.gscorp.bankLion.editor.LocalDatePropertyEditor;
import br.com.gscorp.bankLion.entity.TipoUsuario;
import br.com.gscorp.bankLion.entity.Usuario;
import br.com.gscorp.bankLion.exception.UsuarioException;
import br.com.gscorp.bankLion.repository.BancoRepository;
import br.com.gscorp.bankLion.repository.UsuarioRepository;
import br.com.gscorp.bankLion.security.AuthorityConstant;
import br.com.gscorp.bankLion.security.BankLionSecurityContext;
import br.com.gscorp.bankLion.security.BankLionUserDetails;
import br.com.gscorp.bankLion.service.UsuarioService;

@Controller
@Transactional
@RequestMapping("/usuario")
@Profile("BANKLION")
public class UsuarioController {
	
	@Autowired
	private UsuarioRepository repository;

	@Autowired
	private UsuarioService usuarioService;
	
	@Autowired
	private BancoRepository bancoRepository;
	
	ShaPasswordEncoder passwordEncoder = new ShaPasswordEncoder(256);

	/**
	 * Define configurações para serialização de datas
	 * 
	 * @param binder
	 */
	@InitBinder
	public void initBinder(final WebDataBinder binder) {

		binder.registerCustomEditor(LocalDate.class, new LocalDatePropertyEditor("dd/MM/yyyy"));
	}

	@RequestMapping(value="/logado", method=RequestMethod.GET)
	@ResponseBody
	public BankLionUserDetails verificarUsuarioLogado(Authentication auth) {
		if(auth != null)
		{
			return (BankLionUserDetails) auth.getPrincipal();
		} else {
			throw UsuarioException.SESSAO_EXPIRADA;
		}
	}

	@RequestMapping(value="/solicitarsenha", method=RequestMethod.POST)
	@ResponseStatus(value=HttpStatus.OK)
	@ResponseBody
	public Boolean solicitarSenha(@RequestParam("email") String email) throws IOException {
		Usuario usuario = repository.findByUsuarioIgnoreCase(email);

		if(usuario == null) {
			throw UsuarioException.USUARIO_INVALIDO;
		}
		
		if(usuario.getHash() == null || usuario.getHash().equals("")){
			String hash = usuarioService.gerarHash();
			usuario.setHash(hash);
			repository.save(usuario);
		}

		return true;
	}

	@RequestMapping(value="/confirmarcadastro", method=RequestMethod.GET)
	@ResponseBody
	public Usuario confirmarCadastro(@RequestParam("hash") String hash) {
		Usuario usuario = repository.findByHash(hash);

		if(usuario == null) {
			throw UsuarioException.HASH_INVALIDO;
		}

		usuario.setAtivo(true);
		usuario.setHash(null);
		repository.save(usuario);
		
		return usuario;
		
	}

	@RequestMapping(value="/alterarsenha", method=RequestMethod.PUT)
	@ResponseBody
	public Usuario alterarSenha(@RequestParam("hash") String hash, @RequestParam("senha") String senha) {
		Usuario usuario = repository.findByHash(hash);

		if(usuario == null) {
			throw UsuarioException.HASH_INVALIDO;
		}

		usuario.setSenha(passwordEncoder.encodePassword(senha, null));
		usuario.setHash(null);
		usuario.setAtivo(true);
		repository.save(usuario);
		
		return usuario;
	}

	@RequestMapping(value="/novasenha", method=RequestMethod.PUT)
	@ResponseBody
	public Usuario novaSenha(@RequestParam("senhaAtual") String senhaAtual, @RequestParam("novaSenha") String novaSenha) {
		String senhaMD5 = passwordEncoder.encodePassword(senhaAtual, null);
		Usuario usuario = repository.findByIdAndSenha(BankLionSecurityContext.getUsuarioLogado().getId(), senhaMD5);

		if(usuario == null) {
			throw UsuarioException.SENHA_ATUAL_INVALIDA;
		}

		usuario.setSenha(passwordEncoder.encodePassword(novaSenha, null));
		usuario.setHash(null);
		usuario.setAtivo(true);
		repository.save(usuario);

		BankLionSecurityContext.getUsuarioLogado();
		
		return usuario;
	}

	@RequestMapping(method=RequestMethod.POST)
	@ResponseBody
	@ResponseStatus(value = HttpStatus.CREATED)
	public Usuario salvar(@Valid @ModelAttribute CadastroUsuarioForm form) {
		Usuario usuario = form.converterFormToUsuario();
		usuario.setAgencia(form.getAgencia());
		usuario.setTipoUsuario(TipoUsuario.USUARIO);
		usuario.setBanco(bancoRepository.findOne(form.getBancoId()));
		String hash = usuarioService.gerarHash();
		usuario.setHash(hash);
		usuario.setAtivo(true);

		Usuario usuarioComMesmoUsername = repository.findByUsuarioIgnoreCase(form.getUsuario());
		if(usuarioComMesmoUsername!= null){
			throw UsuarioException.USUARIO_EXISTENTE;
		}

		return repository.save(usuario);
	}

	@RequestMapping(value="/{id}", method=RequestMethod.PUT)
	@ResponseBody
	@Secured(value={AuthorityConstant.ROLE_USUARIO})
	public Usuario atualizar(@PathVariable("id") Long id, @Valid @ModelAttribute UsuarioForm form) {
		Usuario usuario = repository.findOne(id);
		usuario.setNome(form.getNome());
		usuario.setEmail(form.getEmail());
		usuario.setAtivo(true);
		usuario.setDataCadastro(LocalDateTime.now());
		usuario.setUsuario(form.getUsuario());
		usuario.setGerente(form.getGerente());
		usuario.setNumeroConta(form.getNumeroConta());
		usuario.setSaldo(form.getSaldo());
		usuario.setSalario(form.getSalario());
		usuario.setAgencia(form.getAgencia());
		usuario.setBanco(bancoRepository.findOne(form.getBancoId()));
		
		return repository.save(usuario);
	}


	@RequestMapping(value="/{id}", method=RequestMethod.DELETE)
	@ResponseBody
	@Secured(value={AuthorityConstant.ROLE_ADMINISTRADOR})
	public void remover(@PathVariable("id") Long id) {
		Usuario usuario = repository.findOne(id);
		usuario.setAtivo(false);
		repository.save(usuario);
	}

	@RequestMapping(value="/{id}", method=RequestMethod.GET)
	@ResponseBody
	@Secured(value={AuthorityConstant.ROLE_USUARIO})
	public Usuario recuperar(@PathVariable("id") Long id) {
		return repository.findOne(id);
	}

	@RequestMapping(method=RequestMethod.GET)
	@ResponseBody
	public List<Usuario> listar() {
		return (List<Usuario>) repository.findAll();
	}
}
